{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "dc59e068",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from scipy import stats\n",
    "import pymysql\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "plt.rcParams['font.family']='SimHei'\n",
    "plt.rcParams['axes.unicode_minus']=False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "f6b97e11",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>user_id</th>\n",
       "      <th>timestamp</th>\n",
       "      <th>group</th>\n",
       "      <th>landing_page</th>\n",
       "      <th>converted</th>\n",
       "      <th>date</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>851104</td>\n",
       "      <td>2017-01-21 22:11:48.556739</td>\n",
       "      <td>control</td>\n",
       "      <td>old_page</td>\n",
       "      <td>0</td>\n",
       "      <td>2017-01-21</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>804228</td>\n",
       "      <td>2017-01-12 08:01:45.159739</td>\n",
       "      <td>control</td>\n",
       "      <td>old_page</td>\n",
       "      <td>0</td>\n",
       "      <td>2017-01-12</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>661590</td>\n",
       "      <td>2017-01-11 16:55:06.154213</td>\n",
       "      <td>treatment</td>\n",
       "      <td>new_page</td>\n",
       "      <td>0</td>\n",
       "      <td>2017-01-11</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>853541</td>\n",
       "      <td>2017-01-08 18:28:03.143765</td>\n",
       "      <td>treatment</td>\n",
       "      <td>new_page</td>\n",
       "      <td>0</td>\n",
       "      <td>2017-01-08</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>864975</td>\n",
       "      <td>2017-01-21 01:52:26.210827</td>\n",
       "      <td>control</td>\n",
       "      <td>old_page</td>\n",
       "      <td>1</td>\n",
       "      <td>2017-01-21</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   user_id                   timestamp      group landing_page  converted  \\\n",
       "0   851104  2017-01-21 22:11:48.556739    control     old_page          0   \n",
       "1   804228  2017-01-12 08:01:45.159739    control     old_page          0   \n",
       "2   661590  2017-01-11 16:55:06.154213  treatment     new_page          0   \n",
       "3   853541  2017-01-08 18:28:03.143765  treatment     new_page          0   \n",
       "4   864975  2017-01-21 01:52:26.210827    control     old_page          1   \n",
       "\n",
       "         date  \n",
       "0  2017-01-21  \n",
       "1  2017-01-12  \n",
       "2  2017-01-11  \n",
       "3  2017-01-08  \n",
       "4  2017-01-21  "
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = pd.read_csv('ab_data.csv')\n",
    "# data['date']=pd.to_datetime(data['timestamp']).dt.date\n",
    "data[\"date\"] = data.timestamp.str[:10]\n",
    "data.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9376e33f",
   "metadata": {},
   "outputs": [],
   "source": [
    "alpha = 0.05\n",
    "beta = 0.2\n",
    "k = 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "7f8adece",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.6448536269514722"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Z1_alpha = stats.norm.ppf(1-alpha,loc=0,scale=1)\n",
    "Z1_alpha"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8bc2fbdc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.8416212335729143"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Z1_beta = stats.norm.ppf(1-beta,loc=0,scale=1)\n",
    "Z1_beta"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "61539227",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.1203863045004612"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# pa是对照组老页面（control，old_page）的转化率，由于我们无法获取实际的老页面转化率，则只能认定对照组老页面的转化率为pa。\n",
    "pa = data.converted[(data.group==\"control\") & (data.landing_page==\"old_page\")].mean()\n",
    "pa"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0086d48e",
   "metadata": {},
   "source": [
    "$\n",
    "n_A = kn_B\\ and\\ n_B = (\\frac{\\pi_A(1-\\pi_A)}{k} + \\pi_B(1-\\pi_B))(\\frac{z_{1- \\alpha} +  z_{1-\\beta}}{\\pi_A - \\pi_B} )^2\n",
    "$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "0e320973",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.2203863045004612 0.1303863045004612 0.1213863045004612\n"
     ]
    }
   ],
   "source": [
    "# pb是实验组新页面（treatment,new_page）的转化率，在样本量计算阶段，我们无法获取该数据，但是由于pb对样本量的影响较大，特别是pb趋近于pa时，需要的样本量将大大增强。\n",
    "# 本次通过三个pb值估算样本量，然后取比较合理的值，且设定的二类指标为pb>pa，则设定pb-pa=0.1，0.01，0.001，得出三个pb值（pb_t1,pb_t2,pb_t3）\n",
    "pb_t1=pa+0.1\n",
    "pb_t2=pa+0.01\n",
    "pb_t3=pa+0.001\n",
    "print(pb_t1,pb_t2,pb_t3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "efa7a6ea",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "18.0 1415.0 137130.0\n"
     ]
    }
   ],
   "source": [
    "# 分别计算na_t1,na_t2,na_t3\n",
    "na_t1=round(((pa*(1-pa))+pb_t1*(1-pb_t1))*np.power(((Z1_alpha-Z1_beta)/(pa-pb_t1)),2),0)\n",
    "na_t2=round(((pa*(1-pa))+pb_t2*(1-pb_t2))*np.power(((Z1_alpha-Z1_beta)/(pa-pb_t2)),2),0)\n",
    "na_t3=round(((pa*(1-pa))+pb_t3*(1-pb_t3))*np.power(((Z1_alpha-Z1_beta)/(pa-pb_t3)),2),0)\n",
    "print(na_t1,na_t2,na_t3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "76823b5a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "group      landing_page\n",
       "control    new_page          1928\n",
       "           old_page        145274\n",
       "treatment  new_page        145311\n",
       "           old_page          1965\n",
       "Name: user_id, dtype: int64"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data.groupby([\"group\",\"landing_page\"])[\"user_id\"].count()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "1f07704f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 选择其中一天的数据进行统计，选择的原则是AB测试期间中间那一天的日期。\n",
    "date = '2017-01-14'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 109,
   "id": "69d60bba",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>group</th>\n",
       "      <th>landing_page</th>\n",
       "      <th>converted</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>control</td>\n",
       "      <td>new_page</td>\n",
       "      <td>0.137931</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>control</td>\n",
       "      <td>old_page</td>\n",
       "      <td>0.126756</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>treatment</td>\n",
       "      <td>new_page</td>\n",
       "      <td>0.119242</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>treatment</td>\n",
       "      <td>old_page</td>\n",
       "      <td>0.127660</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       group landing_page  converted\n",
       "0    control     new_page   0.137931\n",
       "1    control     old_page   0.126756\n",
       "2  treatment     new_page   0.119242\n",
       "3  treatment     old_page   0.127660"
      ]
     },
     "execution_count": 109,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 计算指定日期不同组的平均转化率\n",
    "df = data[data.date==date].groupby([\"group\",\"landing_page\"],as_index=False)[\"converted\"].mean()\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5af8dbf",
   "metadata": {},
   "source": [
    "计算统计量 $ \\bar x_B- \\bar x_A$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "id": "8b74dcc5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-0.007513837211454807"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 计算统计量pa,pb,以及实验组与对照组的差值tBA\n",
    "tBA = df.converted[2] - df.converted[1]\n",
    "pA = df.converted[1]\n",
    "pB = df.converted[2]\n",
    "tBA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "id": "a321b499",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6548 6600\n"
     ]
    }
   ],
   "source": [
    "# 计算对照组和实验组在指定日期的样本数量\n",
    "nA = data[data.date==date].converted[(data.group==\"control\") & (data.landing_page==\"old_page\")].count()\n",
    "nB = data[data.date==date].converted[(data.group==\"treatment\") & (data.landing_page==\"new_page\")].count()\n",
    "print(nA,nB)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d019833",
   "metadata": {},
   "source": [
    "求$ \\frac {s_A ^2}{n_A} + \\frac {s_B ^2}{n_B} $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 112,
   "id": "339908d7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.38085252401084e-05"
      ]
     },
     "execution_count": 112,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 由于数据统计时，转化为1，未转化为0，该统计可以认为是0-1分布，则统计的方差为p(1-p)\n",
    "varAB = pA*(1-pA)/nA+pA*(1-pA)/nA\n",
    "varAB"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f41e4970",
   "metadata": {},
   "source": [
    "$ \\bar x_B- \\bar x_A$ ~ N(0, varAB)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "id": "1d39e598",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.9018658051460563"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 计算显著性值pBA\n",
    "pBA = 1-stats.norm.cdf(tBA,0,np.sqrt(varAB))\n",
    "pBA"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "01d3b03b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "实验组比对照组转化率无明显提升\n"
     ]
    }
   ],
   "source": [
    "# 将pBA与alpha进行比较，得出结果\n",
    "if pBA>alpha:\n",
    "    print('实验组比对照组转化率无明显提升')\n",
    "else:\n",
    "    print('实验组比对照组转化率有明显提升')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 116,
   "id": "f62321f4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>group</th>\n",
       "      <th>converted</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>80</th>\n",
       "      <td>control</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>85</th>\n",
       "      <td>treatment</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>145</th>\n",
       "      <td>control</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>168</th>\n",
       "      <td>control</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>173</th>\n",
       "      <td>treatment</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "         group  converted\n",
       "80     control          1\n",
       "85   treatment          0\n",
       "145    control          1\n",
       "168    control          0\n",
       "173  treatment          0"
      ]
     },
     "execution_count": 116,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 对传入函数的DataFrame表进行处理\n",
    "temp  = data[data.date==date].loc[((data.group==\"control\")&(data.landing_page==\"old_page\"))|((data.group==\"treatment\")&(data.landing_page==\"new_page\")),[\"group\",\"converted\"]]\n",
    "temp.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "id": "32ddf3b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#     df = temp\n",
    "#     alpha = 0.05\n",
    "#     group_col=False\n",
    "#     value_col=False\n",
    "def ABTest_proportion(df: pd.DataFrame, alpha=0.05, group_col: str = None, value_col: str = None):\n",
    "    '''\n",
    "    :param df: 被分析DateFrame对象\n",
    "    :param alpha: 临界值\n",
    "    :param group_col: 组列的名字，默认为df的第一列\n",
    "    :param value_col: 值列的名字,默认为df的第2列\n",
    "    :return:tBA,pAB,temp_result\n",
    "       tBA:实验组统计量-对照组统计量\n",
    "       pAB:显著性\n",
    "       temp_result:测试结果\n",
    "    '''\n",
    "    if not group_col:\n",
    "        group_col = df.columns[0]\n",
    "    if not value_col:\n",
    "        value_col = df.columns[1]\n",
    "        \n",
    "    temp_mean = df.groupby(group_col,as_index=False)[value_col].mean()\n",
    "    temp_num = df.groupby(group_col,as_index=False)[value_col].count()\n",
    "    tBA =temp_mean.iloc[0,1] - temp_mean.iloc[1,1]\n",
    "        \n",
    "    varAB =temp_mean.iloc[0,1] *(1-temp_mean.iloc[0,1])/temp_num.iloc[0,1] +  temp_mean.iloc[1,1] *(1-temp_mean.iloc[1,1])/temp_num.iloc[1,1]\n",
    "# 原题为右侧单侧检验，在这里补充左侧单侧检验以及双侧检验的结果。\n",
    "    pBA_right = 1-stats.norm.cdf(tBA,0,np.sqrt(varAB))\n",
    "    pBA_left = stats.norm.cdf(tBA,0,np.sqrt(varAB))\n",
    "    pBA_both = float(np.minimum(pBA_left,pBA_right))*2;\n",
    "    \n",
    "    temp_result = [['右侧检验',pBA_right,np.where(pBA_right<alpha,'显著','不显著'),tBA],\n",
    "                   ['左侧检验',pBA_left,np.where(pBA_left<alpha,'显著','不显著'),tBA],\n",
    "                   ['双侧检验',pBA_both,np.where(pBA_both<alpha,'显著','不显著'),tBA]]\n",
    "    temp_DataFrame = pd.DataFrame(temp_result,columns =['检测方向','显著性','结果','统计量差值'])\n",
    "    return temp_DataFrame\n",
    "#     display(temp_mean,temp_num,tBA,varAB,temp_result,temp_DataFrame)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "id": "1b75d4a9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>检测方向</th>\n",
       "      <th>显著性</th>\n",
       "      <th>结果</th>\n",
       "      <th>统计量差值</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>右侧检验</td>\n",
       "      <td>0.094822</td>\n",
       "      <td>不显著</td>\n",
       "      <td>0.007514</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>左侧检验</td>\n",
       "      <td>0.905178</td>\n",
       "      <td>不显著</td>\n",
       "      <td>0.007514</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>双侧检验</td>\n",
       "      <td>0.189644</td>\n",
       "      <td>不显著</td>\n",
       "      <td>0.007514</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   检测方向       显著性   结果     统计量差值\n",
       "0  右侧检验  0.094822  不显著  0.007514\n",
       "1  左侧检验  0.905178  不显著  0.007514\n",
       "2  双侧检验  0.189644  不显著  0.007514"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ABTest_proportion(temp)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "217c5141",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
